Contents

  1. You want to do what?!?
  2. Getting started
  3. The desktop
  4. A pseudo-base class
  5. Joy and grief
  6. Conclusions / Lessons Learned

Joy and grief

or

Implementing window-shade is fun, but why does it flicker so?

Here's the mouse-event handling for folder windows (the DesktopFrame class). mouseClicked() was fun to write (and debug). It's still missing an <Option>-click equivalent to display the folder path in the title bar. That would be fun.
public class DesktopFrame extends DesktopWindow implements Serializable, MouseListener, MouseMotionListener, FilenameFilter {
	// snip

	public void mouseDragged( MouseEvent e ) {
		Desktop.setTopFrame( this );
		Desktop.adjustClipRect( this.getBounds() );

		if ( this.drag ) {
			this.setLocation( this.getBounds().x - this.initialX + e.getX(), this.getBounds().y - this.initialY + e.getY() );
		}
		else if ( this.getResize() ) {
			this.setSize( this.getBounds().width - this.resizeX + e.getX(), this.getBounds().height - this.resizeY + e.getY() );

			panel.reshape( this.getInsets().left, this.getInsets().top, this.getBounds().width - this.getInsets().right - this.getInsets().left, this.getBounds().height - this.getInsets().top - this.getInsets().bottom );
			this.pack();
			this.validate();
			this.resizeX = e.getX();
			this.resizeY = e.getY();
		}
	}

	public void mouseMoved( MouseEvent e ) {
	}

	public void mouseClicked( MouseEvent e ) {
		if ( e.getClickCount() == 1 ) {
			Desktop.setTopFrame( this );

			// Check the close box.
			if ( this.getCloseBox().contains( e.getPoint() ) ) {
				this.setVisible( false );
				Desktop.removeWindow( this );
			}
			// Check the shade box.
			// Note the logic to handle the non-zoomed state.
			else if ( this.getShadeBox().contains( e.getPoint() ) ) {
				if ( !this.getShade() ) {
					this.prevHeightShade = this.getBounds().height;
					this.setHeight( this.getCloseBox().getBounds().y + this.getCloseBox().getBounds().height + 5 );
					this.setShade( true );
				}
				else {
					if ( !this.getZoom() )
						this.setHeight( this.prevHeightShade );
					else
						this.setHeight( this.prevHeight );

					panel.reshape( this.getInsets().left, this.getInsets().top, this.getBounds().width - this.getInsets().right - this.getInsets().left, this.getBounds().height - this.getInsets().top - this.getInsets().bottom );

					this.pack();
					this.validate();

					this.setShade( false );
				}
			}
			// Check the zoom box.
			else if ( this.getZoomBox().contains( e.getPoint() ) ) {
				Toolkit tk = Toolkit.getDefaultToolkit();
				Dimension d = tk.getScreenSize();
				// Set Window properties based on screen size.
				int res = tk.getScreenResolution();

				if ( !this.getZoom() ) {
					this.prevX = this.getBounds().x;
					this.prevY = this.getBounds().y;
					this.prevWidth = this.getBounds().width;
					
					if ( this.getShade() )
						this.prevHeight = this.prevHeightShade;
					else
						this.prevHeight = this.getBounds().height;
						
					this.setBounds( 0, Global.defaultMenuBarHeight, d.width, d.height - Global.defaultMenuBarHeight );

					panel.reshape( this.getInsets().left, this.getInsets().top, this.getBounds().width - this.getInsets().right - this.getInsets().left, this.getBounds().height - this.getInsets().top - this.getInsets().bottom );

					this.pack();
					this.validate();
					this.setShade( false );
					this.setZoom( true );
				}
				// Once again, strange but necessary logic awaits:
				// check if the window was already shaded.
				else {
					if ( this.getShade() )
						this.setHeight( this.prevHeight );

					this.setBounds( this.prevX, this.prevY, this.prevWidth, this.prevHeight );

					panel.reshape( this.getInsets().left, this.getInsets().top, this.getBounds().width - this.getInsets().right - this.getInsets().left, this.getBounds().height - this.getInsets().top - this.getInsets().bottom );

					this.pack();
					this.validate();
					this.setShade( false );
					this.setZoom( false );
				}
			}
		}
		// Handle double-clicks in the title bar: shading.
		else if ( e.getClickCount() == 2 ) {
			Rectangle r = new Rectangle( 0, 0, this.getBounds().width, this.getCloseBox().getBounds().y + this.getCloseBox().getBounds().height + 5 );
			if ( r.contains( e.getPoint() ) ) {
				if ( !this.getShade() ) {
					this.prevHeightShade = this.getBounds().height;
					this.setHeight( this.getCloseBox().getBounds().y + this.getCloseBox().getBounds().height + 5 );
					this.setShade( true );
				}
				else {
					if ( !this.getZoom() )
						this.setHeight( this.prevHeightShade );
					else
						this.setHeight( this.prevHeight );

					panel.reshape( this.getInsets().left, this.getInsets().top, this.getBounds().width - this.getInsets().right - this.getInsets().left, this.getBounds().height - this.getInsets().top - this.getInsets().bottom );

					this.pack();
					this.validate();
					this.setShade( false );
				}
			}
		}
	}

	public void mouseEntered( MouseEvent e ) {
	}

	public void mouseExited( MouseEvent e ) {
	}

	public void mousePressed( MouseEvent e ) {
		// Bring this window to the front, and shuffle the window order (see Desktop.setTopFrame() at bottom of page).
		Desktop.setTopFrame( this );
		
		FontMetrics theFontMetrics = this.getGraphics().getFontMetrics(); 
		this.titleBar.setBounds( this.getCloseBox().getBounds().x + this.getCloseBox().getBounds().width + 2, 0, this.getBounds().width - 8,  theFontMetrics.getHeight() );

		if ( this.getTitleBar().contains( e.getPoint() ) ) {
			this.initialX = e.getX();
			this.initialY = e.getY();
			this.drag = true;
			return;
		}
		
		this.sizeBox.setBounds( this.getBounds().width - Global.defaultGrowBoxSize, this.getBounds().height - Global.defaultGrowBoxSize, Global.defaultGrowBoxSize, Global.defaultGrowBoxSize );

		if ( this.sizeBox.contains( e.getPoint() ) ) {
			this.resizeX = e.getX();
			this.resizeY = e.getY();
			this.setResize( true );
			return;
		}
	}

	public void mouseReleased( MouseEvent e ) {
		if ( this.drag ) {
			this.drag = false;
			this.repaint();
		}
		else if ( this.getResize() ) {
			this.setResize( false );
			this.repaint();
		}
	}

	// snip: the usual assortment of accessors

	public void restoreHeight() {
		if ( this.getShade() )
			this.setHeight( this.prevHeightShade );
	}

	public void setBounds( Rectangle r ) {
		super.setBounds( r );
		panel.reshape( this.getInsets().left, this.getInsets().top, r.width - this.getInsets().right - this.getInsets().left, r.height - this.getInsets().top - this.getInsets().bottom );
	}
}

As promised, here's Desktop.setTopFrame();
	public static void setTopFrame( DesktopFrame d ) {
		boolean found = false;
		int i = 0;

		// Try to find the window in question.
		while ( i < vectorWindows.size() ) {
			if ( vectorWindows.elementAt( i ) == d ) {
				d.toFront();
				d.repaint();
				found = true;
				break;
			}
			
			i++;
		}

		if ( !found )
			return;

		// Once found, shuffle the window order.
		DesktopFrame temp = ( DesktopFrame )vectorWindows.elementAt( i );

		while ( i < vectorWindows.size() - 1 ) {
			vectorWindows.setElementAt( vectorWindows.elementAt( i + 1 ), i );
			i++;
		}

		vectorWindows.setElementAt( temp, vectorWindows.size() - 1 );
	}
PreviousNext